Linux开发

推荐列表 站点导航

当前位置:首页 > 服务器技术 > Linux开发 >

以Linux下的测试程序说明递归型互斥量和普通互斥量的区别

来源:网络整理  作者:  发布时间:2020-12-22 22:09
先贴代码和测试结果: // Mutex.h: 对pthread的互斥量的RAII包装 #ifndef _MUTEX_H_ #define _MUTEX_H_ #include stdio.h #include stdlib.h #...

先贴代码和测试结果:

// Mutex.h: 对pthread的互斥量的RAII包装

#ifndef _MUTEX_H_

#define _MUTEX_H_

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

// 使用错误码errnum和字符串msg来打印错误信息, 并且退出程序

static inline void errExitEN(int errnum, const char* msg)

{

fprintf(stderr, "%s Error: %s\n", msg, strerror(errnum));

exit(1);

}

class Mutex

{

public:

explicit Mutex()

{

int s;

pthread_mutexattr_t attr;

s = pthread_mutexattr_init(&attr);

if (s != 0)

errExitEN(s, "pthread_mutexattr_init");

s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);

#ifdef ERRORCHECK

s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);

#elif RECURSIVE

s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

#endif

if (s != 0)

errExitEN(s, "pthread_mutexattr_settype");

pthread_mutex_init(&__mtx, &attr);

if (s != 0)

errExitEN(s, "pthread_mutex_init");

s = pthread_mutexattr_destroy(&attr);

if (s != 0)

errExitEN(s, "pthread_mutexattr_destroy");

}

virtual ~Mutex()

{

int s = pthread_mutex_destroy(&__mtx);

if (s != 0)

errExitEN(s, "pthread_mutex_destroy");

}

void lock()

{

int s = pthread_mutex_lock(&__mtx);

if (s != 0)

errExitEN(s, "pthread_mutex_lock");

}

void unlock()

{

int s = pthread_mutex_unlock(&__mtx);

if (s != 0)

errExitEN(s, "pthread_mutex_unlock");

}

private:

pthread_mutex_t __mtx;

};

#endif

 

####################################

// MutexTest.cpp: Mutex类对于重复获取同一把锁的测试

#include "Mutex.h"

#include <stdio.h>

#include <pthread.h>

#include <array>

Mutex mtx;

std::array<int, 10> g_array;

// 将g_array[index]左边的元素自增(使用互斥量来保护)

void incrLeftWithMutex(int index)

{

mtx.lock();

for (int i = 0; i < index; i++)

g_array[i]++;

mtx.unlock();

}

// 将g_array[index]右边的元素自增(使用互斥量来保护)

void incrRightWithMutex(int index)

{

mtx.lock();

for (int i = index + 1; i < (int) g_array.size(); i++)

g_array[i]++;

mtx.unlock();

}

// 将g_array[index]以外的元素自增

void incrOtherItem(int index)

{

mtx.lock();

incrLeftWithMutex(index);

incrRightWithMutex(index);

mtx.unlock();

}

int main()

{

g_array.fill(0);

incrOtherItem(5);

for (int i : g_array)

printf("%d ", i);

printf("\n");

return 0;

}

 

####################################

$ g++ MutexTest.cpp -std=c++11 -pthread

$ time ./a.out 

^C

real    0m3.973s

user    0m0.004s

sys    0m0.000s

$ g++ MutexTest.cpp -std=c++11 -pthread -DERRORCHECK

$ ./a.out 

pthread_mutex_lock Error: Resource deadlock avoided

pthread_mutex_destroy Error: Device or resource busy

$ g++ MutexTest.cpp -std=c++11 -pthread -DRECURSIVE

$ ./a.out 

1 1 1 1 0 1 1 1 1

 

不额外定义宏则使用默认的互斥量(锁),定义宏ERRORCHECK则锁自带错误检查,定义宏RECURSIVE则代表递归锁。

主线程中调用了incrOtherItem函数,该函数先获取(acquire)锁mtx,然后调用另外2个函数后释放(release)锁mtx。

实验结果显示默认锁陷入了死锁,错误检查的结果是resource deadlock avoided(即陷入了死锁),而递归锁则成功执行了下去。

因为向一把已经被获取的锁申请上锁时,线程会阻塞一直到已获取锁的一方将锁释放。所以若线程已经获取了锁A而未释放,当它再次获取锁A时会陷入死锁,因为此线程会阻塞直到锁A被释放,然后只有拥有锁的线程(也就是它自己)才能释放锁,而线程自己处于阻塞中,所以永远处于阻塞状态。

 

递归锁就是为了解决这种状况,从incrOtherItem的函数定义看起来代码没任何问题,但是incrLeftWithMutex和incrRightWithMutex函数试图获取了同一把锁,这样相当于未释放锁就再次获取同一把锁。

递归锁会在内部维护一个计数器,当线程第1次获取互斥量时,计数器置为1,之后该线程可以在此获取同一把锁,每次获取锁计数器加1,每次释放锁计数器减1。由于此时其他线程无法获取锁,所以只要保证该线程的执行过程是可重入的,代码就没问题。

 

由于这种情况往往是函数递归调用时才出现的,比如

函数1:上锁,调用函数2,解锁。

函数2:上锁……解锁

函数1的过程就变成了:上锁,函数1的内容(第一部分),上锁,函数2的内容,解锁,函数1的内容(第二部分),解锁。

如果函数1的内容是不可重入的,而函数2修改了函数1的操作对象,那么这里就会出问题。

比如。函数1是想获取全局int数组(设为int a[4] = { 1,2,3,4 } )的总和,第一部分是求前半部分的和,第二部分是求后半部分的和。

而函数2若导致int数组发生了变化,比如让a[2] = 0,这样最后求得的和就是1+2+0+4=7而不是1+2+3+4=10。

相关热词: Linux

本站内容来源于网络,如有侵权请与我们联系,我们会及时删除,我们深感抱歉!
注:本站所有信息仅供用于网络技术学习参考,学习中请遵循相关法律法规!

本文地址: https://v30.fanwenzhu.com/server/kaifa/7521.shtml

相关文章
Copyright © www.juheyunku.com      关于 | 合作 | 声明 | 联系 | 更新 | 地图 | Tags

以Linux下的测试程序说明递归型互斥量和普通互斥量的区别

2020-12-22 编辑:

先贴代码和测试结果:

// Mutex.h: 对pthread的互斥量的RAII包装

#ifndef _MUTEX_H_

#define _MUTEX_H_

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

// 使用错误码errnum和字符串msg来打印错误信息, 并且退出程序

static inline void errExitEN(int errnum, const char* msg)

{

fprintf(stderr, "%s Error: %s\n", msg, strerror(errnum));

exit(1);

}

class Mutex

{

public:

explicit Mutex()

{

int s;

pthread_mutexattr_t attr;

s = pthread_mutexattr_init(&attr);

if (s != 0)

errExitEN(s, "pthread_mutexattr_init");

s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);

#ifdef ERRORCHECK

s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);

#elif RECURSIVE

s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

#endif

if (s != 0)

errExitEN(s, "pthread_mutexattr_settype");

pthread_mutex_init(&__mtx, &attr);

if (s != 0)

errExitEN(s, "pthread_mutex_init");

s = pthread_mutexattr_destroy(&attr);

if (s != 0)

errExitEN(s, "pthread_mutexattr_destroy");

}

virtual ~Mutex()

{

int s = pthread_mutex_destroy(&__mtx);

if (s != 0)

errExitEN(s, "pthread_mutex_destroy");

}

void lock()

{

int s = pthread_mutex_lock(&__mtx);

if (s != 0)

errExitEN(s, "pthread_mutex_lock");

}

void unlock()

{

int s = pthread_mutex_unlock(&__mtx);

if (s != 0)

errExitEN(s, "pthread_mutex_unlock");

}

private:

pthread_mutex_t __mtx;

};

#endif

 

####################################

// MutexTest.cpp: Mutex类对于重复获取同一把锁的测试

#include "Mutex.h"

#include <stdio.h>

#include <pthread.h>

#include <array>

Mutex mtx;

std::array<int, 10> g_array;

// 将g_array[index]左边的元素自增(使用互斥量来保护)

void incrLeftWithMutex(int index)

{

mtx.lock();

for (int i = 0; i < index; i++)

g_array[i]++;

mtx.unlock();

}

// 将g_array[index]右边的元素自增(使用互斥量来保护)

void incrRightWithMutex(int index)

{

mtx.lock();

for (int i = index + 1; i < (int) g_array.size(); i++)

g_array[i]++;

mtx.unlock();

}

// 将g_array[index]以外的元素自增

void incrOtherItem(int index)

{

mtx.lock();

incrLeftWithMutex(index);

incrRightWithMutex(index);

mtx.unlock();

}

int main()

{

g_array.fill(0);

incrOtherItem(5);

for (int i : g_array)

printf("%d ", i);

printf("\n");

return 0;

}

 

####################################

$ g++ MutexTest.cpp -std=c++11 -pthread

$ time ./a.out 

^C

real    0m3.973s

user    0m0.004s

sys    0m0.000s

$ g++ MutexTest.cpp -std=c++11 -pthread -DERRORCHECK

$ ./a.out 

pthread_mutex_lock Error: Resource deadlock avoided

pthread_mutex_destroy Error: Device or resource busy

$ g++ MutexTest.cpp -std=c++11 -pthread -DRECURSIVE

$ ./a.out 

1 1 1 1 0 1 1 1 1

 

不额外定义宏则使用默认的互斥量(锁),定义宏ERRORCHECK则锁自带错误检查,定义宏RECURSIVE则代表递归锁。

主线程中调用了incrOtherItem函数,该函数先获取(acquire)锁mtx,然后调用另外2个函数后释放(release)锁mtx。

实验结果显示默认锁陷入了死锁,错误检查的结果是resource deadlock avoided(即陷入了死锁),而递归锁则成功执行了下去。

因为向一把已经被获取的锁申请上锁时,线程会阻塞一直到已获取锁的一方将锁释放。所以若线程已经获取了锁A而未释放,当它再次获取锁A时会陷入死锁,因为此线程会阻塞直到锁A被释放,然后只有拥有锁的线程(也就是它自己)才能释放锁,而线程自己处于阻塞中,所以永远处于阻塞状态。

 

递归锁就是为了解决这种状况,从incrOtherItem的函数定义看起来代码没任何问题,但是incrLeftWithMutex和incrRightWithMutex函数试图获取了同一把锁,这样相当于未释放锁就再次获取同一把锁。

递归锁会在内部维护一个计数器,当线程第1次获取互斥量时,计数器置为1,之后该线程可以在此获取同一把锁,每次获取锁计数器加1,每次释放锁计数器减1。由于此时其他线程无法获取锁,所以只要保证该线程的执行过程是可重入的,代码就没问题。

 

由于这种情况往往是函数递归调用时才出现的,比如

函数1:上锁,调用函数2,解锁。

函数2:上锁……解锁

函数1的过程就变成了:上锁,函数1的内容(第一部分),上锁,函数2的内容,解锁,函数1的内容(第二部分),解锁。

如果函数1的内容是不可重入的,而函数2修改了函数1的操作对象,那么这里就会出问题。

比如。函数1是想获取全局int数组(设为int a[4] = { 1,2,3,4 } )的总和,第一部分是求前半部分的和,第二部分是求后半部分的和。

而函数2若导致int数组发生了变化,比如让a[2] = 0,这样最后求得的和就是1+2+0+4=7而不是1+2+3+4=10。

本站内容来源于网络,如有侵权请与我们联系,我们会及时删除,我们深感抱歉!
注:本站所有信息仅供学习参考!
本文地址为 https://v30.fanwenzhu.com/server/kaifa/7521.shtml

相关文章

风云图片

推荐阅读

返回Linux开发频道首页